一、這篇想傳達的
二、開場問題
三、軟體開發的學習難題
四、該怎麼解決?理解並梳理思考脈絡
五、理解思考脈絡的本質,注意學習的陷阱
六、結論
第一篇針對了「被動學習」和「主動學習」,傳達了看不到終點的學習焦慮和煩惱,主要來自於自己的學習認知和狀態。
接下來希望透過這篇,傳達學習程式技術的方向問題,為何主要源自於思考脈絡的雜亂,導致學了東,忘了西,學了一堆,等到需要拼在一起綜合運用的時候,腦袋一片空白。
除此之外,第二篇內容會延伸第一篇提到的內容:如何混用專案式學習和實驗式學習。
以下幾位正面臨學習煩惱的人,描述了他們的學習方式和煩惱,先將他們的描述放進腦海當中,看完本文之後,思考看看,除了建議對方直接躺平,你會怎麼點出對方的問題?
A君:工作需要接觸 Angular 框架
我是某公司的 Junior 前端工程師,接下來需要接手幾個 Angular 的專案,在前兩個月,我很努力的把官方文件的內容都一個一個看完並且跟著做,第三個月開始,我準備接手一個 Angular 專案,裡面的程式碼都看過,但是每次需要調整某個頁面的功能、新增某個頁面的時候,卻不知道從何下手,我是不是要回去複習 Angular 的官方文件?
B君:從 .NET 6 框架轉換到 Spring Boot 2.7 框架
我是公司的後端工程師,在公司已經待了一年,由於公司的專案,主要使用的後端框架除了 C#.NET 6,還有 Java Spring Boot 2.7,因此主管希望我去接觸和學習,但我沒有 Java 的基礎,而且 Spring 又有 Spring Boot、Spring WebFlux、Spring Data、Spring Security 這麼多要學,我到底該從何學習?
C君:準備踏入前端工程師
我每天花了很多時間觀看網路上的 HTML & CSS 教學,把所有的 HTML 標籤和 CSS 語法記下來,但我總是容易搞混不同的標籤和語法,<head> 跟 <body> 差在哪、<link> 跟 <script> 差別在哪?CSS 的 vertical-align 和 justify-content 差別在哪?
D君:工作需要接觸 Linux
我是某個初階後端工程師,因為許多的專案都是架設在 Linux 環境,因為聽大家說鳥哥的書超級讚,結果買來之後看了一個月,發現要記的指令和常用資料夾路徑超多,越是學到後面,前面學過的指令和觀念越是沒有印象,我需要從頭回去複習前面的內容嗎?
從上述四位的描述當中,你能否想像他們所學的東西,在腦中是什麼樣子?思考路徑不僅混亂,而且觀念抽象沒有連結。
為什麼?多數有方向和混亂的問題,因為在學習時,聚焦在學習的內容,忽略或甚至輕忽學習的思考脈絡。
假設一個學了變數、條件判斷、輸入和輸出語法,但沒有方向完成 Console 版猜數字遊戲(1~100 猜一個整數)的人做類比,他們腦中的思考脈絡大概長這個樣子:
這樣的思考脈絡究竟有哪些問題?
大腦的思考路徑,不知道主要先想哪些,其次想哪些。
最直接的結果:不知道從何下手,腦袋一片空白。
對應到第一張思考脈絡圖:
每條思考路徑的粗細、顏色、深淺都一樣,隨著學的東西越來越多,思考路徑也容易越來越跳躍。
程式開發這個領域,許多的觀念、語法,對大腦來說都是相當的「抽象」而且「沒感覺」。如果反覆死記硬背,記到後來不只難以回想,又容易遺忘。
對應到第一張思考脈絡圖:
每條思考路徑之間分散且沒有連貫,隨著學的東西越來越多,思考路徑也容易變得越來越難發現。
如果要使用 C# 完成一個 Console 的猜數字遊戲,第一層要想的事情有哪些?
- **首先,怎麼從 1~ 100 的整數,產生並記錄正確答案**
- **接著,玩家怎麼輸入答案,並記錄在程式**
- **再來,如何比對玩家的答案與正確答案一樣**
- **最後,怎麼輸出比對的結果,告知玩家答對還是答錯**
當我把第一層的思考脈絡整理之後,最後會是下圖的樣子。第一層思考路徑,就像這顆樹一樣,一開始從中間的樹幹,從下到上,依序往兩側的樹枝思考。
一旦主次和順序整理出來之後,每次回想猜數字遊戲的時候,都能很快的從這四個問題切入。
回到剛剛猜數字遊戲的例子,第一層脈絡整理出了,猜數字遊戲要依序解決的問題情境。接著依序思考,每個問題情境需要用到哪些程式觀念和語法?
- **首先,怎麼從 1~ 100 產生並記錄正確答案**
- 亂數:使用 Random 產生亂數
- int 型態:產生完的整數亂數,使用整數(int)型態的變數存放
- **接著,玩家怎麼輸入答案,並記錄在程式**
- 輸入的函數 Console.ReadLine()
- 輸入的資料使用字串型態的變數存放 (string)
- **再來,如何比對玩家的答案與正確答案一樣**
- 條件判斷: if {} else {}、三元運算子
- 比較運算: if (userAnswer == inputAnswer)
- 型態轉換:字串跟整數比較前需要將字串轉成整數型態 int.Parse() 或 Convert.ToInt32()
- **最後,怎麼輸出比對的結果,告知玩家答對還是答錯**
- 輸出函數:使用 Console.WriteLine()
當我把第二層的思考脈絡整理之後,最後會是下圖的樣子。
學完整理出完整的脈絡後,原本抽象而且沒感覺的語法,和第一層的問題情境有了連結,有助於語法觀念的記憶和聯想。
接著就可以依照自己的需要,深入學習和實驗每個語法觀念。例如:我想要瞭解更多亂樹的用法,可以新增一隻實驗用的程式專案,去瞭解和實驗每個亂數的函數。因為有了脈絡之後再看語法觀念,跟你一開始直接看語法觀念的感覺會完全不一樣。
備註:因為這篇要把思考脈絡的「路徑」強調出來,所以使用樹來做比喻,你也可以使用心智圖來整理。
繼續延伸猜數字遊戲的例子:
如果今天改從 C# 換到在瀏覽器的 Console,使用弱型別的 JavaScript 實現,你會發現哪些差別?第二層的程式語法和觀念細節。
1. **亂數產生的函數**:Math.random()。 【語法差異】
2. **int 型態**:使用 let 宣告,而且不需要型態。 【語法差異+語言特性】
3. **輸入函數**:如果是瀏覽器的 prompt() 當作輸入函數。 【語法差異】
4. **型態轉換**:parseInt()。 【語法差異】
5. **比較運算**:除了嚴格相等 (===),即使型態不同,弱型別的語言也會自動轉換。 【語言特性】
6. **輸出函數**:console.log()
在轉換相似的思考脈絡當中,你也整理了當中的語法差異和語言特性差異。同樣的還可以練習看看:
如果要從 Console 改成網頁表單的形式,原本的脈絡會怎麼變化?這邊以**第一層脈絡**的「輸入答案」和「比對答案」,使用原生 JavaScript 為例:
- ~~輸入函數~~ **表單**:欄位的說明 (label) 、輸入的欄位 (input)送出按鈕(Button))
- **事件(event)**:表單送出事件(submit) 或按鈕事件 (click)
- **取得 input 的內容**:submit 事件的 event.target\['xxx'].value 或使用 document.querySelector('yyy').value 從 DOM 下手
當你學習時,從整理的脈絡發現,對於表單、DOM、事件的觀念不太熟悉時,你就可以抽一些時間,深入學習這幾個部分的整體觀念。
除了用來轉換到不同的語言或工具,還可以加入變化,如果覺得抽象,可以自己嘗試照著上面的原則,「主要先想那些,哪些是次要接著想的,觀念和問題該怎麼連結。」將思考脈絡條列並畫出來:
在 Console 的猜數字遊戲,加上「可以重複猜測直到答對」、「最多猜測十次」、「最大最小的範圍提示」、「再玩一輪」等功能。
- **可以重複猜測直到答對**:
在第一層脈絡上,多了一條分支「可以重複猜測直到答對」,往下延伸一條第二層分支: while 迴圈,重複的條件為「使用者答案 不等於 正確答案」 (userAnswer != correctAnswer)。
- **最多猜測十次**:
- 第一層「計算並判斷作答次數」,底下延伸一條「算術運算」。
- 接著回到第一層,想到剛才的「可以重複猜測直到答對」,重複的條件改為「『使用者答案 不等於 正確答案』,而且『猜測的次數 小於等於 5 (初始值從1開始)』」(userAnswer != correctAnswer && guessTime <= 5)。
- **最大最小的範圍提示**:
第一層先想到「比對答案」,接著想到「條件判斷」,原本的程式邏輯只有「答對(\==)」、「答錯(!=)」,加上提示範圍的程式邏輯變成「答對(\==)」、「比答案小(<=)」、「比答案大(>=)」。
- **再玩一輪**:
第一層脈絡,多了一條分支「再玩一輪」,底下延伸兩條第二層分支,包含 「do...while 迴圈」和「輸入函數」。
對應第一篇提到的專案式學習和實驗式學習,在專案式練習,關注三個部分:
當你透過實驗式學習,深入瞭解某一個路徑的觀念時,用相同的原則,將某個路徑從原先的脈絡拉出來思考,整理成另一個抽象語法或觀念的思考脈絡。
往後在回想的時候,不管你從問題情境的角度切入往下延伸,或者從抽象語法觀念的角度切入往前回想,思考脈絡對你來說整體又容易掌握。
語法或觀念細節要怎麼辦?
整理思考脈絡的重點在,整理思考的路徑,所以流程或語法觀念的細節,比較建議整理成文章、程式檔的形式。不在第二篇的討論範圍。
最多延伸到幾層?每一個層級要固定放什麼?
沒有標準答案,只要思考時,路徑能自然的連貫起來。
想不到一開始的切入點
從問題情境或實現的功能切入並拆解。
如果認為發現實在有難度,我會直接建議找專案導向的書或課程 + Google 學習。
鑽牛角尖,或者過於發散。
通常是想的「太細」,或者「不夠具體直接」,可以試著拉高層級,整理歸納較為細節的脈絡。
某個思考路徑,超出自身的認知,卡住無法往下延伸。
思考跟目前的學習重點有關,如果連結性較小,選擇性的暫時擱置。
以上三點拿開場問題的B君學習 Spring Boot 為例,沒整理過的思考脈絡可能長這個樣子:
1. Java 的 JDK 與 JRE
2. IDE 或編輯器
3. Maven 或 Gradle 的使用
4. 怎麼啟動 Spring Boot 的 API 測試
5. 怎麼定義 GET, POST, PUT, PATCH, DELETE 的 API
6. API 的請求參數和回應參數怎麼定義和存取。
7. 連線資料庫的設定。
8. 資料表與欄位的 Class 定義。
9. SQL 操作在 Hibernate 的架構下該怎麼實作。
10. 實作基本的會員相關功能
...
經過整理後的思考脈絡:
1. 開發前的準備:開發環境、開發工具
- Java 的 JDK 與 JRE
- 開發用的 IDE 或編輯器
2. 怎麼建立和啟動一個專案
- 怎麼使用 Maven 或 Gradle 安裝套件
- 使用什麼指令啟動 API 測試。
3. 以前寫過的會員系統,在 Spring Boot 是怎麼實作的
- 如何定義 WebAPI
- 怎麼定義 GET, POST, PUT, PATCH, DELETE 的 API
- API 的請求參數和回應參數怎麼定義和存取。
- 相依服務的設定和使用
- 定義為可被注入的相依服務類別 (建立 Bean)
- 注入其他的相依服務類別 (注入 Bean)
- 搭配關連式資料庫
- 連線資料庫的前置設定。
- 資料表與欄位的 Class 定義。 (Model)
- SQL 基本 CRUD 操作在 Hibernate 的架構下該怎麼實作。 (Repository)
- 怎麼實作關聯查詢 (Join)
- 怎麼使用資料庫的交易 (Transaction)
- 註冊的商業邏輯
- 判斷帳號是否重複註冊
- 判斷密碼的強度
- 判斷密碼與確認密碼是否一樣
- 密碼的加鹽
- 登入的商業邏輯
...
- 登出的商業邏輯
...
- 忘記密碼的商業邏輯
...
如果你不像 B 君有足夠的經驗,延伸幾點學習上的建議:
記錄下來當前常常卡住的部分,抽時間一起學習
如果在學習 Spring Boot 的時候,發現 SQL、@Autowird 和 @Component 等等觀念和用法,常常讓你覺得卡東卡西,我會記錄下來,花點時間把卡住的觀念和語法整個學過一遍,另外開個實驗專案嘗試並觀察。
準備一至兩個自己能從頭到尾完成,而且相對完整的作品,找個練功的第一份工作
練習的過程中,或許你的作法並不是 Best Practice,Code 寫的不是很好,或者有些脈絡是自學不容易接觸到的,但至少你有了屬於自己的學習脈絡和紀錄。
先挑一個要求沒這麼高的低階工作練功,進入職場之後,跟隨較資深的前輩做中學,將有毒的作法、設計調整過來,並且補足自學不容易掌握的思考脈絡。很多對程式碼好壞很敏銳的人,也是接手過別人的爛 Code,或者自己的爛 Code。如果是一位天份不錯的人,自認作品的 Level 比其他人高,那第一份工作就可以挑比較好的去挑戰,因人而異。
看完了思考脈絡的說明和舉例,我需要特別強調,思考脈絡是給自己用的。換句話說:
只有靠自己思考和整理的思考脈絡,印象才會最深刻,思考起來才會靈活。
因此我會建議在剛開始學習程式的時候,最好避免:
抄捷徑的密技和方法,適合在有一定思考脈絡的時候,補足不足的部分,但如果當作一開始主要學習的內容,反而是大腦整理思考脈絡的殺手。為什麼?
工作面臨的開發與除錯問題,很多都是綜合性的問題,沒有脈絡的密技和技巧,很容易會以東拼西湊的角度切入,遇到問題越補越大洞、方向越來越偏。
專案式學習面對的問題情境、完成的功能,在學習的時候幫助蒐集、釐清、整理思考脈絡,混用實驗式學習,可以在有整體脈絡的支撐下,專心強化所學的理論。
看文本篇內容之後,我們來整理一下文章的思考脈絡:
除了學習,工作要接手陌生的專案、文章或會議紀錄的大綱、找程式問題,有沒有習慣整理思考脈絡,做事的效率也會有明顯的差別。
最後以開場問題的 A 君來進行總結:
A 君可以從網站的角度切入第一層脈絡,Angular 前端框架主要處理的問題有哪一些(以下是簡單舉例):
- 開發的前置作業:開發環境的設定
- 管理網站底下的所有頁面位置:網站的路由
- 元件開發
- 頁面 UI 元件
- 共用 UI 元件
- API 串接的服務層
- 整個網站的資料共用:
- 服務 (Service)
- 狀態管理 (NgRx)
- 多語系
- 環境變數
- 建置與部署
隨著時間,不斷累積開發處理的問題和情境,加上框架本身的更新變化,A君對於 Angular 的思考脈絡也會跟著改變。不同的人,對於 Angular 的思考脈絡也不會一樣。
C、D君,兩位的思考脈絡,就留給讀者自己整理看看。
題外話:
曾經下班為了練習 Angular 13,透過完成 UI Libary 的過程,熟悉 Angular 13 大部分的 UI 元件的語法,有興趣可以參考看看,怎麼轉成新版的 Standalone & Control Flow 寫法:
https://ngx-youi.github.io/NGX-YOUI/
第二篇內容到這邊結束,謝謝大家耐心的閱讀。